import json
import os
import re
import traceback
import threading
import uuid

import shortuuid
import base64
import pymysql
import pdfplumber
from pdf2docx import Converter
from copy import deepcopy
import json_repair
from decimal import Decimal
from bid.generate.tools.dawate import main_stream

# Create your views here.
from django.db.models import Q, F
from django.http import JsonResponse, StreamingHttpResponse, HttpResponse
from django.utils.encoding import iri_to_uri

#django模型
# from bid.generate.models import ExBidProject, ExBidPmProject, ExBid, ExProject, ExBidTeam, ExTeamProject, ExTeamWork,\
#                                 ExTeamQualification, ExBidSocialInsurance, ExTeam, ExTeamSocialInsurance, ExBidParse, ExFile,\
#                                 ExCompany, ExBidCatalog, ExDept
from bid.generate.models import *

from zentao.settings import BIG_SAVE_FOLDER, PYMYSQL_CONF, ROOT_FOLDER, AGENT_QUEUE

#业绩生成
from bid.generate.tools.generate_project import generate_project

#负责人业绩生成
from bid.generate.tools.generate_pmProject import generate_pmProject

#团队生成
from bid.generate.tools.generate_team import generate_team

#招标文件抽取
from bid.generate.tools.bid_parse import bid_parse_func

#根据招标文件生成业绩和团队
from bid.generate.tools.ai_generate_project_team import ai_generate_project_team

#sql转为vue前端展示格式
from bid.generate.tools.sql2vue import sql2vue

#技术文件目录及文件生成
from bid.generate.tools.tech_generate import tech_catalog_generate, tech_generate, tech_catelog_content_generate, tree2word

#商务文件生成
from bid.generate.tools.business_generate import business_generate

#全局变量
global_thread = {
    'project': {},
    'pmProject': {},
    'team': {},
    'bid_parse': {},
    'tech': {},
    'business': {}
}

import time
from functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__!r} executed in {(end_time - start_time)*1000:.2f}ms")
        return result
    return wrapper

#招标文件解析线程
class bid_parse_thread(threading.Thread):
    def __init__(self, path, save_path, bid_parse_info, task_id, _type,unique_id):
        super(bid_parse_thread, self).__init__()
        self.path = path
        self.save_path = save_path
        self.bid_parse_info = bid_parse_info
        self.task_id = task_id
        self._type = _type
        self.unique_id = unique_id

    def run(self):

        # 如果没有agent,则循环等待
        while 'agent' not in global_thread[self._type][self.task_id].keys():

            # 每10s查询一次
            time.sleep(10)

            if not AGENT_QUEUE.empty():
                global_thread[self._type][self.task_id]['agent'] = AGENT_QUEUE.get()
                global_thread[self._type][self.task_id]['status'] = '进行中'

        try:
            #更新状态
            query = Q()
            query &= Q(id=self.bid_parse_info['id'])
            # query &= Q(createBy=self.bid_parse_info['createBy'])
            # query &= Q(name=self.bid_parse_info['name'])
            # query &= Q(type=self.bid_parse_info['type'])
            # query &= Q(createDate=self.bid_parse_info['createDate'])
            ExBidParse.objects.filter(query).update(status='doing')

            #pdf转word
            self.pdf_find_page()

            #招标文件解析
            st = time.time()
            bid_parse_res = bid_parse_func(self.save_path, global_thread[self._type][self.task_id]['agent'],self.unique_id)
            print(time.time()-st)
            #先使用本地文件读取
            # with open('/media/977GB/wcj_work/禅道/新视图开发/zentao/json化结果.json', 'r') as f:
            #     bid_parse_res = json.load(f)

            #获取招标项目名称和采购编号
            try:
                projectName = bid_parse_res['基础信息']['项目信息']['招标项目名称']
            except:
                projectName = ''

            try:
                projectCode = bid_parse_res['基础信息']['项目信息']['采购编号/项目编号']
            except:
                projectCode = ''

            #开标时间
            bidOpenTime = bid_parse_res['基础信息']['关键时间节点']['开标时间']
            #获取事件数字
            number_ls = re.findall(r'\d+', bidOpenTime, re.DOTALL)
            if len(number_ls) == 3:
                bidOpenTime = f"{'-'.join(number_ls[:3])}"
            elif len(number_ls) == 6:
                bidOpenTime = f"{'-'.join(number_ls[:3])} {':'.join(number_ls[3:])}"
            else:
                bidOpenTime = None

            # 标的标包存储
            self.bid_package(self.bid_parse_info['id'], bid_parse_res,
                             global_thread[self._type][self.task_id]['agent'])

            #保存到数据库
            query = Q()
            query &= Q(id=self.bid_parse_info['id'])
            # query &= Q(createBy=self.bid_parse_info['createBy'])
            # query &= Q(name=self.bid_parse_info['name'])
            # query &= Q(type=self.bid_parse_info['type'])
            # query &= Q(createDate=self.bid_parse_info['createDate'])
            ExBidParse.objects.filter(query).update(text=json.dumps(bid_parse_res,ensure_ascii=False),
                                                    status='done',
                                                    projectName=projectName,
                                                    projectCode=projectCode,
                                                    bidOpenTime=bidOpenTime
                                                    )

            #释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])

        except:

            # 保存到数据库
            query = Q(id=self.bid_parse_info['id'])
            ExBidParse.objects.filter(query).update(status='failure')

            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
            print(f"{self.task_id}\t招标文件解析失败")
            print(traceback.format_exc())

    #调用大瓦特
    def dawate_streaming(self, content, config_params):
        config_params['messages'] = [{"role": "user", "content": content}]
        answer = ''
        response_content = main_stream(config_params)
        try:
            while True:
                a = next(response_content)
                answer += a
                # sys.stdout.write(f"\r{[a]}")
                # sys.stdout.flush()
                print(a, end='')
        except StopIteration:
            pass

        # 回答去除think部分
        return answer.split('</think>')[-1]

    #提取json部分
    def json_process(self, data_answ):
        pattern = r'```json(.*?)```'
        matches = re.findall(pattern, data_answ, re.DOTALL)  # 抽取出内容并判断长度
        if matches:
            c_statement = matches[-1]
            # print("\n-------step2查到的对应的内容为：\n", c_statement)
        else:
            c_statement = '{}'
        # decoded_object = json_repair.loads(c_statement)
        # return json.dumps(decoded_object,ensure_ascii=False)
        return c_statement

    #将标的标包存到数据库中
    def bid_package(self, bid_parse_id, bid_parse_res, config_params):
        # 给大模型的数据
        llm_data = {"详细评审标准": {}, "专用资格要求": {}, "标的标包清单": {}}
        # if '详细评审标准' in bid_parse_res.keys() and bid_parse_res['详细评审标准']:
        detail_review = deepcopy(bid_parse_res['详细评审标准'])
        detail_review_ls = {}
        cnt = 1
        for item in detail_review:
            for key in item.keys():
                if '详细评审标准' in key:
                    item = item[key]
                    break

            package = item.pop('适用标包')
            # 表表可能抽为列表，转为字符串
            if isinstance(package, list):
                package = ''.join(package)
            detail_review_ls[package] = item
            llm_data['详细评审标准'][f"{cnt}"] = package
            cnt += 1

        if '专用资格要求' in bid_parse_res.keys() and bid_parse_res['专用资格要求']:
            cnt = 1
            special_qua = deepcopy(bid_parse_res['专用资格要求'])
            special_qua_ls = {}

            for item in special_qua:
                package = item.pop('关联标的/标包/标段')
                special_qua_ls[package] = item['要求']
                llm_data['专用资格要求'][f"{cnt}"] = package
                cnt += 1

        bid_packages = deepcopy(bid_parse_res['标的清单及分包情况/招标范围'])
        bid_package_ls = {}
        cnt = 1
        for bid_package in bid_packages:
            for package in bid_package['包括的标包为']:
                bid_package_ls[f"{cnt}"] = {"标的":bid_package['标的名称'].replace('\n','').replace('\t','')
                                            , "标包": package['标包名称'].replace('\n','').replace('\t','')}
                llm_data['标的标包清单'][f"{cnt}"] = f"标的：{bid_package['标的名称']}[标包：{package['标包名称']}]"
                cnt += 1

        # with open('内容.json', 'w') as f:
        #     json.dump(llm_data, f, ensure_ascii=False)
        #
        # time.sleep(100000)

        prompt = '''
        ###要求###
        根据传入的详细评审标准、专用资格要求和标的标包清单，根据内容为标的标包清单中每一个都分配一个详细评审标准和专用资格要求的序号
        
        ###注意###
        详细评审标准中需要标的和标包同时对应，专用资格要求中如果只列出了标的，则意味着这个专用资格要求适用于这个标的下的所有标包
        详细评审标准不能为空，标的标包必须要有对应的详细评审标准
        专用资格要求如果未匹配到，字符串为空即可，返回格式为json
        以下仅为示例数据，实际结果按照标包数量来，有多少标包就要有多少条：
        
        ###输入###
        {
          "详细评审标准": {
            "1": "标1:营配融合APP 一网通办作业安全管控数字应用提升[包1]",
            "2": "标2:低压业扩优化管控数字应用提升[包1]"
          },
          "专用资格要求": {
          "1": "标1:营配融合APP一网通办作业安全管控数字应用提升"
          },
          "标的标包清单": {
            "1": "标1:营配融合APP一网通办作业安全管控数字应用提升[包1]",
            "2": "标2:低压业扩优化管控数字应用提升[包1]",
          }
        }
        
        ###输出###
        ```json
        {
        "1":{"详细评审标准": "1"， "专用资格要求":"1"},
        "2":{"详细评审标准": "2"， "专用资格要求":""},
        }
        ```
        '''
        query = f"{prompt}以下为输入内容：\n{json.dumps(llm_data, ensure_ascii=False)}"
        # print(query)
        data_answ = self.dawate_streaming(query, config_params)
        data = json_repair.loads(self.json_process(data_answ))


        #更新数据库
        target2id = {}
        for key, value in data.items():
            detail = None
            special = None
            if value['详细评审标准'] and value['详细评审标准'] in llm_data['详细评审标准']:
                detail = detail_review_ls[llm_data['详细评审标准'][value['详细评审标准']]]
            if value['专用资格要求'] and value['专用资格要求'] in llm_data['专用资格要求']:
                special = special_qua_ls[llm_data['专用资格要求'][value['专用资格要求']]]
            target_name = bid_package_ls[key]['标的']
            bid_package = bid_package_ls[key]['标包']

            #添加
            #查询bid_parse对象
            bid_parse = ExBidParse.objects.filter(id=bid_parse_id).first()

            if target_name in target2id.keys():
                #如果存过标的，则存储标包时parent为target的id
                target_id = target2id[target_name]
            else:
                #如果没存过标的，则先存储标的，再存储标包时parent为target的id
                target = ExBidPackage.objects.create(bid_parse=bid_parse,
                                            parent=None,
                                            name=target_name,
                                            review=None,
                                            special=None)
                target_id = target.id
                target2id[target_name] = target_id

            ExBidPackage.objects.create(bid_parse=bid_parse,
                                        parent=target_id,
                                        name=bid_package,
                                        review=detail,
                                        special=special)

    # 查找位置
    def conv_save(self, start_page, end_page):
        # print('start_page, end_page:', start_page, end_page)
        # if isinstance(start_page, int) and isinstance(end_page, int):
        #     print("step1:找到招标文件所在位置：", start_page, end_page)
        # else:
        #     print("error,step1:未找到招标文件所在位置")
        # file_name = str(start_page) + '-' + str(end_page) + str(
        #     datetime.now().strftime('%Y-%m-%d-%H-%M-%S')) + filename + '-output.docx'
        # docx_file = os.path.join(out_path, file_name)
        # 初始化转换器
        cv = Converter(self.path)
        # 转换PDF中的所有页面 232-347
        cv.convert(self.save_path, start=start_page, end=end_page)
        # 关闭转换器
        cv.close()

    def pdf_find_page(self):
        """
        找出PDF中模板文件所在的位置
        :return:
        """
        # 打开PDF文件
        s_s = None
        s_e = None
        j_s = None
        j_e = None
        # with pdfplumber.open('./招投标文件生成(data)/01招标/1_南方电网公司2024年第四批信息化项目招标文件.pdf') as pdf:
        with pdfplumber.open(self.path) as pdf:
            # 读取每一页的内容
            # print(len(pdf.pages))
            for page in pdf.pages:  # page <Page:1>
                # if page=="<Page:231>":
                text = page.extract_text()
                text = text.strip().replace("\n", "").replace(" ", "")
                # print('文件文本：',page,text,type(text))
                if "投标文件格式及相关附件" in text:
                    s_s = int(str(page).replace("<Page:", "").replace(">", ""))  # 商务文件起始位置
                    # print("投标文件格式及相关附件:",page)
                    break
                # if "技术投标文件" in text and s_s:
                #     if len(text) < 20:
                #         s_e = int(str(page).replace("<Page:", "").replace(">", "")) - 1
                #         j_s = int(str(page).replace("<Page:", "").replace(">", ""))
                #         # print("技术投标文件:",page,len(text))
                # if '报价投标文件' in text and j_s:
                #     if len(text) < 20:
                #         j_e = int(str(page).replace("<Page:", "").replace(">", ""))

            # print(f'其他文件{0}-{s_s}')
            # print(f'商务文件{s_s}-{s_e}')
            # print(f'技术文件{j_s}-{j_e}')
            # print(f'报价文件{j_e}-{len(pdf.pages)}')
            self.conv_save(0, s_s)
            # conv_save(s_s, s_e, filename='招标商务文件')
            # conv_save(j_s, j_e, filename='招标技术文件')
            # conv_save(j_e, len(pdf.pages), filename='招标报价文件')

#根据招标文件解析生成业绩和团队
class project_team_thread(threading.Thread):
    def __init__(self, bid_id, task_id, _type):
        super(project_team_thread, self).__init__()
        self.bid_id = bid_id
        self.task_id = task_id
        self._type = _type

    def run(self):

        # 如果没有agent,则循环等待
        while 'agent' not in global_thread[self._type][self.task_id].keys():

            # 每10s查询一次
            time.sleep(10)

            if not AGENT_QUEUE.empty():
                global_thread[self._type][self.task_id]['agent'] = AGENT_QUEUE.get()
                global_thread[self._type][self.task_id]['status'] = '进行中'

        # 更新状态
        ExBid.objects.filter(id=self.bid_id).update(status='doing')

        try:
            ai_generate_project_team(self.bid_id, global_thread[self._type][self.task_id]['agent'])

            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])

            #更新状态
            ExBid.objects.filter(id=self.bid_id).update(status='done')

        except:
            # 更新状态
            ExBid.objects.filter(id=self.bid_id).update(status='failure')
            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
            print(f"{self.bid_id}\t任务文件生成失败")
            print(traceback.format_exc())

# 业绩生成线程
class project_thread(threading.Thread):
    def __init__(self, project_data, extra_data, save_path, task_id, bid_id):
        super(project_thread, self).__init__()
        self.project_data = project_data
        self.extra_data = extra_data
        self.save_path = save_path
        self.task_id = task_id
        self.bid_id = bid_id

    def run(self):

        try:
            generate_project(self.project_data, self.extra_data, self.save_path, self.bid_id)
        except:
            print(f"{self.task_id}\t任务业绩生成失败")
            print(traceback.format_exc())

# 负责人业绩生成线程
class pmProject_thread(threading.Thread):
    def __init__(self, project_data, extra_data, save_path, task_id, bid_id):
        super(pmProject_thread, self).__init__()
        self.project_data = project_data
        self.extra_data = extra_data
        self.save_path = save_path
        self.task_id = task_id
        self.bid_id = bid_id

    def run(self):

        try:
            generate_pmProject(self.project_data, self.extra_data, self.save_path, self.bid_id)
        except:
            print(f"{self.task_id}\t任务负责人业绩生成失败")
            print(traceback.format_exc())

# 团队生成线程
class team_thread(threading.Thread):
    def __init__(self, team_info,team_bidSocialInsurances, save_path, task_id, bid_id):
        super(team_thread, self).__init__()
        self.team_info = team_info
        self.team_bidSocialInsurances = team_bidSocialInsurances
        self.save_path = save_path
        self.task_id = task_id
        self.bid_id = bid_id

    def run(self):

        try:
            generate_team(self.team_info,self.team_bidSocialInsurances, self.save_path, self.bid_id)
        except:
            print(f"{self.task_id}\t团队生成失败")
            print(traceback.format_exc())

# 技术文件目录生成线程
class tech_catalog_generate_thread(threading.Thread):
    def __init__(self, path, _id, task_id):
        super(tech_catalog_generate_thread, self).__init__()
        self.path = os.path.join(ROOT_FOLDER, path)
        self._id = _id
        self.task_id = task_id

    def run(self):

        try:
            tech_catalog_generate(self.path, self._id)
        except:
            print(f"{self.task_id}\t技术文件目录生成失败")
            print(traceback.format_exc())

# 商务、技术文件生成线程
class business_tech_generate_thread(threading.Thread):
    def __init__(self, _type, catalogs, bid, account, task_id):
        super(business_tech_generate_thread, self).__init__()
        self._type = _type
        self.catalogs = catalogs
        self.bid = bid
        self.account = account
        self.task_id = task_id

    def run(self):

        # 如果没有agent,则循环等待
        while 'agent' not in global_thread[self._type][self.task_id].keys():

            # 每10s查询一次
            time.sleep(10)

            if not AGENT_QUEUE.empty():
                global_thread[self._type][self.task_id]['agent'] = AGENT_QUEUE.get()
                global_thread[self._type][self.task_id]['status'] = '进行中'

        try:
            if self._type == 'tech':
                # 更新状态为进行中
                ExBid.objects.filter(id=self.bid).update(techStatus='doing')

                tech_generate(self.catalogs, global_thread[self._type][self.task_id]['agent'])

                # 更新状态为完成
                ExBid.objects.filter(id=self.bid).update(techStatus='done')
            else:
                # 更新状态为进行中
                ExBid.objects.filter(id=self.bid).update(businessStatus='doing')

                business_generate(self.catalogs, self.bid, self.account, global_thread[self._type][self.task_id]['agent'],self.task_id)

                # 更新状态为完成
                ExBid.objects.filter(id=self.bid).update(businessStatus='done')

            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
        except:
            if self._type == 'tech':
                # 更新状态为完成
                ExBid.objects.filter(id=self.bid).update(techStatus='failure')
            else:
                # 更新状态为完成
                ExBid.objects.filter(id=self.bid).update(businessStatus='failure')
            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
            print(f"{self.task_id}\t技术文件生成失败")
            print(traceback.format_exc())

# 技术文件单个章节生成
class tech_single_generate_thread(threading.Thread):
    def __init__(self, title, _id, task_id, _type):
        super(tech_single_generate_thread, self).__init__()
        self.title = title
        self._id = _id
        self.task_id = task_id
        self._type = _type

    def run(self):

        # 如果没有agent,则循环等待
        while 'agent' not in global_thread[self._type][self.task_id].keys():

            # 每10s查询一次
            time.sleep(10)

            if not AGENT_QUEUE.empty():
                global_thread[self._type][self.task_id]['agent'] = AGENT_QUEUE.get()
                global_thread[self._type][self.task_id]['status'] = '进行中'

        try:
            tech_catelog_content_generate(self.title, self._id, global_thread[self._type][self.task_id]['agent'])
            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
        except:
            # 释放
            AGENT_QUEUE.put(global_thread[self._type][self.task_id]['agent'])
            print(f"{self.task_id}\t技术文件生成失败")
            print(traceback.format_exc())

#文本基于url的base64编码(+替换成-，/替换成_，=替换成.)
def url_bs64_encode(string):
    bs64_str = str(base64.b64encode(string.encode("utf-8")), "utf-8")
    return  bs64_str.replace('+','-').replace('/','_').replace('=','.')

#文本基于url的base64解码(-替换成+，.替换成=)
def url_bs64_decode(string):
    bs64_str = string.replace('-','+').replace('_','/').replace('.','=')
    return  base64.b64decode(bs64_str).decode()

#判断是否登录
def has_permission(request):

    # session没有token，则未登录
    session_token = request.session.get('token')
    if not session_token:
        return False

    # 如果未传入token，则未登录
    request_token = request.META.get('HTTP_TOKEN')
    if not request_token:
        return False

    #如果都传入，则比对是否一致
    if session_token == request_token:
        return True
    else:
        return False

# #获取登录用户公司的所有部门
# @timing_decorator
# def api_get_dept(request):
#     if request.method == 'GET':
#
#         # 检查用户是否登录
#         if not has_permission(request):
#             # 如果用户未登录，抛出 AuthenticationFailed 异常
#             return JsonResponse({'code': 0, 'msg': '未登录'})
#
#         #获取登录用户
#         account = request.session['account']
#         #获取登录用户所属的公司
#         team_account = ExTeam.objects.filter(account=account).select_related('company').first()
#
#         #获取公司所有部门
#         depts = ExDept.objects.filter(company=team_account.company).values('id', 'name').distinct()
#
#         res = {
#             'code': 1,
#             'msg': "获取成功",
#             'data': list(depts)
#         }
#
#         return JsonResponse(res)
#
#     else:
#         res = {
#             'code': 0,
#             'msg': "不支持的请求方法"
#         }
#         return JsonResponse(res, status=405)

#招标文件解析
#获取登录用户创建的投标任务
@timing_decorator
def api_bid_parse(request):
    if request.method == 'POST':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        #招标类型
        bid_type = request.POST.get('bidType')
        # 类型
        _type = request.POST.get('type')
        # 上传的文件
        files = request.FILES.getlist('file')

        if not _type or not files or not bid_type:
            return JsonResponse({'code': 0, 'msg': '未携带类型或未上传文件或未提供招标文件类型'})

        if _type != 'bid_parse':
            return JsonResponse({'code': 0, 'msg': '接口类型只能为bid_parse'})

        #只要一个文件
        file = files[0]

        # # 如果有文件，先存储
        # 文件存储位置
        save_folder = os.path.join(BIG_SAVE_FOLDER, _type)
        if not os.path.isdir(save_folder):
            os.makedirs(save_folder)

        now = time.strftime('%Y%m%d_%H%M%S',time.localtime(time.time()))

        # 保存文件
        path = os.path.join(save_folder, f"{now}_{file.name}")
        save_path = os.path.splitext(path)[0] + '.docx'

        # 对每个上传的文件进行处理，例如保存文件
        with open(path, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        #获取用户示例
        team_instance = ExTeam.objects.get(account=request.session['account'])

        #当前时间
        current = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))

        # 插入到ex_file数据库
        bid_parse_file = ExFile.objects.create(path=path.replace(ROOT_FOLDER, ''),
                                               type='pdf',
                                               uploadDate=current)

        # 如果没有该任务，则添加
        if _type not in global_thread.keys():
            global_thread[_type] = {}
        global_thread[_type][task_id] = {}

        # 获取队列中的agent
        if AGENT_QUEUE.empty():
            global_thread[_type][task_id]['status'] = '排队中'
            status = 'queue'
        else:
            global_thread[_type][task_id]['agent'] = AGENT_QUEUE.get()
            global_thread[_type][task_id]['status'] = '进行中'
            status = 'doing'

        # 插入数据库
        bid_parse = ExBidParse.objects.create(name=file.name,
                                              type=bid_type,
                                              status=status,
                                              file=bid_parse_file,
                                              createDate=current,
                                              createBy=team_instance)

        # 插入着一条的id
        bid_parse_id = bid_parse.id

        # 这一条的信息
        bid_parse_info = {
            'id': bid_parse_id,
            'createBy': request.session['account'],
            'name': file.name,
            'type': bid_type,
            'createDate': current
        }

        #唯一id
        unique_id = f"bid_parse_{task_id}"

        # 放到线程中
        job = bid_parse_thread(path, save_path, bid_parse_info, task_id, _type,unique_id)
        job.start()

        res = {
            'code': 1,
            'msg': "上传成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取登录用户解析文件列表
@timing_decorator
def api_bid_parse_list(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        #登录用户
        account = request.session['account']

        #获取登录用户的招标文件（格式化日期）
        bid_parses = ExBidParse.objects.filter(createBy=account).extra(
            select={'createDate': "DATE_FORMAT(createDate, '%%Y-%%m-%%d %%H:%%i:%%s')",
                    'bidOpenTime': "DATE_FORMAT(bidOpenTime, '%%Y-%%m-%%d %%H:%%i:%%s')",}
            ).values('id', 'name', 'type', 'status', 'createDate', 'bidOpenTime', 'projectName', 'projectCode').distinct()

        bid_parse_list = list(bid_parses)

        res = {
            'code': 1,
            'msg': "获取成功",
            'data': bid_parse_list
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取解析文件详情
@timing_decorator
def api_bid_parse_info(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')

        if not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id'})

        #获取该id的文件解析信息
        bid_parse = ExBidParse.objects.filter(id=_id).select_related('file').first()

        if not bid_parse:
            return JsonResponse({'code': 0, 'msg': '不存在该id的解析内容'})

        if not bid_parse.text:
            return JsonResponse({'code': 0, 'msg': '该招标文件没有解析内容'})

        # print(list(json.loads(bid_parse.text).keys()))
        with open('数据库解析内容.json', 'w') as f:
            json.dump(json.loads(bid_parse.text), f, ensure_ascii=False)

        #获取招标文件路径
        path = bid_parse.file.path if bid_parse.file else ''

        # with open('解析结果.json', 'w') as f:
        #     json.dump(json.loads(bid_parse.text), f, ensure_ascii=False)

        res = {
            'code': 1,
            'msg': "获取成功",
            'data': {
                "name": bid_parse.name,
                "content": sql2vue(json.loads(bid_parse.text)),
                "path": url_bs64_encode(path)
            }
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#根据招标文件创建投标任务,获取标的和投标人（公司）
@timing_decorator
def api_bid_create_first_select(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')

        if not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id'})

        # #获取该id的文件解析信息
        # bid_parse = ExBidParse.objects.filter(id=_id).values('text','status').first()
        #
        # if not bid_parse:
        #     return JsonResponse({'code': 0, 'msg': '不存在该id的解析内容'})
        #
        # #如果状态不为done则代表还未解析完成
        # if bid_parse['status'] != 'done':
        #     return JsonResponse({'code': 0, 'msg': '招标文件解析未完成'})
        #
        # # 如果状态为done但text为空则代表还解析失败
        # if bid_parse['status'] == 'done' and not bid_parse['text']:
        #     return JsonResponse({'code': 0, 'msg': '招标文件解析失败'})
        #
        # # 汇总数据
        # result = {
        #     "targetName": [],
        #     "bidder": {}
        # }
        #
        # #获取标的
        # data = json.loads(bid_parse['text'])['标的清单及分包情况/招标范围']
        # for value in data:
        #     result['targetName'].append(value["标的名称"].replace('\n',''))

        #获取解析出来的标的标包
        packages = ExBidPackage.objects.filter(bid_parse=_id, parent=None).values('id', 'name').distinct()

        # 汇总数据
        result = {
            "targetName": [],
            "bidder": {}
        }

        for package in packages:
            result['targetName'].append({"id":package['id'], "name":package["name"]})

        #获取公司名称
        company = ExTeam.objects.filter(account=request.session['account']).select_related('company').first()
        result['bidder']['id'] = company.company.id
        result['bidder']['name'] = company.company.name

        # 获取所有委托代理人
        # agents = ExLegalAgents.objects.filter(company=result['bidder']['id'], type='代理人').values('account', 'name').distinct()
        # result['agents'] = list(agents)

        #获取所有部门
        depts = ExDept.objects.filter(company=result['bidder']['id']).values('id', 'name').distinct()
        result['depts'] = list(depts)


        res = {
            'code': 1,
            'msg': "获取成功",
            'data': result
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#根据招标文件创建投标任务,获取标的对应的标包
def api_bid_create_second_select(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        target_id = request.GET.get('target_id')

        if not _id or not target_id:
            return JsonResponse({'code': 0, 'msg': '未携带招标id或标的id'})

        # #获取该id的文件解析信息
        # bid_parse = ExBidParse.objects.filter(id=_id).values('text').first()
        #
        # if not bid_parse:
        #     return JsonResponse({'code': 0, 'msg': '不存在该id的解析内容'})
        #
        # #汇总数据
        # result = None
        #
        # #获取标的和标包
        # data = json.loads(bid_parse['text'])['标的清单及分包情况/招标范围']
        # for value in data:
        #     # for i in value['包括的标包为']:
        #     #     print([value["标的名称"], i['标包名称']])
        #     if value["标的名称"].replace('\n','') == target:
        #         result = [i['标包名称'].replace('\n','') for i in value['包括的标包为']]
        #
        # if result is None:
        #     return JsonResponse({'code': 0, 'msg': '不存在该标的对应的标包'})

        #获取标包
        result = []
        # 获取解析出来的标的标包
        packages = ExBidPackage.objects.filter(bid_parse=_id, parent=target_id).values('id', 'name').distinct()

        for package in packages:
            result.append({"id": package['id'], "name": package["name"]})

        res = {
            'code': 1,
            'msg': "获取成功",
            'data': result
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

# #获取公司委托代理人
def api_get_agent(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取用户
        account = request.session['account']

        #获取用户公司
        team = ExTeam.objects.filter(account=account).select_related('company').first()

        #获取所有委托代理人
        agents = ExLegalAgents.objects.filter(company=team.company.id, type='代理人').values('account', 'name').distinct()

        res = {
            'code': 1,
            'msg': "获取成功",
            'data': list(agents)
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#创建投标任务
def api_bid_create(request):
    if request.method == 'POST':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 招标文件id
        bid_parse_id = request.POST.get('bid_parse_id')

        if bid_parse_id:
            # 获取招标文件解析
            bid_parse_obj = ExBidParse.objects.filter(id=bid_parse_id).first()
        else:
            bid_parse_obj = None

        # 获取参数
        # 标的名称
        target_id = request.POST.get('target_id')
        # 标包名称
        package_id = request.POST.get('package_id')
        #开标时间
        bidOpenTime = request.POST.get('bidOpenTime')
        #招标项目名称
        projectName = request.POST.get('projectName')
        # 采购编号/项目编号
        projectCode = request.POST.get('projectCode')
        #项目负责人及联系方式
        project_head_phone = request.POST.get('projectHeadPhone')
        #投标人
        bidder = request.POST.get('bidder')
        #部门id
        dept_id = request.POST.get('dept_id')
        #详细评审标准
        # review = request.POST.get('review')
        project_rule = request.POST.get('project_rule')
        pm_project_rule = request.POST.get('pm_project_rule')
        team_rule = request.POST.get('team_rule')
        #专业评审标准
        special = request.POST.get('special') if request.POST.get('special') else None
        # dept_id = 1
        #委托人account
        # agent = request.POST.get('agent')
        # agent = 'likang'
        # 上传的文件
        # files = request.FILES.getlist('file')

        for i in ['target_id', 'package_id', 'bidOpenTime', 'projectName', 'projectCode', 'project_head_phone', 'bidder', 'dept_id']:
            if not eval(i):
                return JsonResponse({'code': 0, 'msg': f'{i}不能为空'})

        #不传招标id时，必须传专业评审标准
        if not bid_parse_id and not (project_rule or pm_project_rule or team_rule):
            return JsonResponse({'code': 0, 'msg': f'不传招标id时，必须传入'})

        #登录用户
        account = request.session['account']

        #获取创建人
        createBy = ExTeam.objects.filter(account=account).first()
        #获取公司
        bidder = ExCompany.objects.filter(id=bidder).first()
        #获取部门
        dept = ExDept.objects.filter(id=dept_id).first()
        #获取委托人
        # agent = ExLegalAgents.objects.filter(account=agent).first()
        if bid_parse_id:
            #获取标的标包
            target = ExBidPackage.objects.filter(id=target_id).first()
            package = ExBidPackage.objects.filter(id=package_id).first()
        else:
            #组合详细评审标准
            project_rule = request.POST.get('project_rule')
            pm_project_rule = request.POST.get('pm_project_rule')
            team_rule = request.POST.get('team_rule')
            review = {}
            if project_rule:
                review['project_rule'] = project_rule
            if pm_project_rule:
                review['pm_project_rule'] = pm_project_rule
            if team_rule:
                review['team_rule'] = team_rule
            review = json.dumps(review, ensure_ascii=False)
            # 先存储标的标包
            # 如果没存过标的，则先存储标的，再存储标包时parent为target的id
            target = ExBidPackage.objects.create(bid_parse=None,
                                                 parent=None,
                                                 name=target_id,
                                                 review=None,
                                                 special=None)

            package = ExBidPackage.objects.create(bid_parse=None,
                                        parent=target.id,
                                        name=package_id,
                                        review=review,
                                        special=special)

        _type = 'project_team'
        # 如果没有该任务，则添加
        if _type not in global_thread.keys():
            global_thread[_type] = {}

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        global_thread[_type][task_id] = {}

        # 获取队列中的agent
        if AGENT_QUEUE.empty():
            global_thread[_type][task_id]['status'] = '排队中'
            status = 'queue'
        else:
            global_thread[_type][task_id]['agent'] = AGENT_QUEUE.get()
            global_thread[_type][task_id]['status'] = '进行中'
            status = 'doing'

        #添加到数据库
        bid = ExBid.objects.create(bidParse=bid_parse_obj,
                                   dept=dept,
                                   # agent=agent,
                                 # targetName=target_name,
                                 # bidPackage=bid_package,
                                 target=target,
                                 package=package,
                                 bidOpenTime=bidOpenTime,
                                 projectName=projectName,
                                 projectCode=projectCode,
                                 projectHeadPhone=project_head_phone,
                                 bidder=bidder,
                                 createBy=createBy,
                                 status=status,
                                 techStatus='wait',
                                 businessStatus='wait'
                                 )

        # st = time.time()
        # print(createBy.company.id)
        # print(time.time()-st)
        # time.sleep(100000)

        # 放到线程中
        job = project_team_thread(bid.id, task_id, _type)
        job.start()

        res = {
            'code': 1,
            'msg': "创建成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取登录用户创建的投标任务
@timing_decorator
def api_get_bid(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        account = request.session['account']

        # 查询投标任务
        query = Q(createBy=account)

        bids = ExBid.objects.filter(query).select_related('pmProject','project','team','bidder','target','package').distinct()

        bids_ls = []

        for bid in bids:
            info = {
                "id": bid.id,
                "tech_upload": True if bid.businessStatus!='wait' else False,
                "targetName": bid.target.name,
                "bidPackage": bid.package.name,
                "bidder": bid.bidder.name,
                "projectName": bid.projectName,
                "projectCode": bid.projectCode,
                "projectHeadPhone": bid.projectHeadPhone,
                "status": bid.status if bid.status else 'wait',
                "tech_status": bid.techStatus,
                "business_status": bid.businessStatus,
                "file": {}
            }

            for key in ['pmProject','project','team']:
                file = eval(f'bid.{key}')
                if file:
                    path = file.path
                    info['file'][key] = {'path': url_bs64_encode(path), 'name': os.path.split(path)[-1],'type': file.type}

            bids_ls.append(info)

        res = {
            'code': 1,
            'msg': "获取成功",
            'data': bids_ls
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取（负责人）业绩信息
# @timing_decorator
# def api_get_project(request):
#     if request.method == 'GET':
#
#         # 检查用户是否登录
#         if not has_permission(request):
#             # 如果用户未登录，抛出 AuthenticationFailed 异常
#             return JsonResponse({'code': 0, 'msg': '未登录'})
#
#         # 获取参数
#         _id = request.GET.get('id')
#         _type = request.GET.get('type')
#
#         if not _id or not _type:
#             return JsonResponse({'code': 0, 'msg': '未携带id或类型'})
#
#         if _type not in ['project', 'pmProject']:
#             return JsonResponse({'code': 0, 'msg': '类型只能为project或pmProject'})
#
#         # 格式化
#         project_data = {}
#
#         # 查询公司业绩
#         query = Q(bid=_id)
#
#         if _type == 'project':
#             bid_projects = ExBidProject.objects.filter(query).all().distinct()
#         else:
#             bid_projects = ExBidPmProject.objects.filter(query).select_related('project').all().distinct()
#
#         # 总金额
#         total_amount = 0
#         total = 0
#
#         # 获取所有项目的id
#         project_ids = set()
#         for i, bid_project in enumerate(bid_projects, start=1):
#
#             # 项目信息
#             project = bid_project.project
#
#             # 如果关联项目不存在，则跳过
#             if not project:
#                 continue
#
#             project_ids.add(project.id)
#
#             # 项目金额
#             contractAmount = project.contractAmount if project.contractAmount else 0
#
#             # 对方联系人
#             counterpartContact = project.counterpartContact if project.counterpartContact else ''
#
#             # 对方电话
#             counterpartPhone = project.counterpartPhone if project.counterpartPhone else ''
#
#             # 总金额
#             total_amount += contractAmount
#             total += 1
#
#             if _type == 'project':
#                 info = {
#                     'id': project.id,
#                     'name': project.name if project.name else '',
#                     'address': project.address if project.address else '',
#                     'counterpartName': project.counterpartName if project.counterpartName else '',
#                     'counterpartContact': f'{counterpartContact}\n{counterpartPhone}',
#                     'amount': contractAmount,
#                     'desc': project.desc if project.desc else '',
#                     'comment': project.comment if project.comment else '',
#                 }
#             else:
#                 # 验收日期
#                 acceptanceDate = project.acceptanceDate
#                 if acceptanceDate:
#                     year = f'{acceptanceDate.year}年'
#                 else:
#                     year = ''
#
#                 # 获取项目负责人情况
#                 # bid = ExBid.objects.filter(id=_id).select_related('createBy').first()
#                 bid_team = ExBidTeam.objects.filter(bid=_id, position='项目负责人').select_related('account').first()
#                 if not bid_team:
#                     return JsonResponse({'code': 0, 'msg': '不存在该id'})
#
#                 # 获取项目负责人信息
#                 pm = bid_team.account
#                 if pm:
#                     pm_name = pm.name
#                     professional = pm.professional
#                 else:
#                     pm_name = ''
#                     professional = ''
#
#                 info = {
#                     'id': project.id,
#                     'name': project.name if project.name else '',
#                     'contractName': project.contractName if project.contractName else '',
#                     'counterpartName': project.counterpartName if project.counterpartName else '',
#                     'pm': pm_name,
#                     'acceptanceYear': year,
#                     'amount': contractAmount
#                 }
#
#             project_data[project.id] = info
#
#         # 获取该任务的生成文件
#         query = Q(id=_id)
#
#         # files = ExFile.objects.filter(query).all().distinct()
#
#         bid = ExBid.objects.filter(query).select_related(_type).first()
#
#         if not bid:
#             return JsonResponse({'code': 0, 'msg': '没有该投标任务'})
#
#         file = eval(f"bid.{_type}")
#
#
#         generate_files = []
#
#         if file:
#             path = file.path
#             generate_files.append({'name': os.path.split(path)[-1],
#                                    'path': url_bs64_encode(path),
#                                    'type': file.type})
#
#         # 转为列表
#         project_data = list(project_data.values())
#
#         res = {
#             'code': 1,
#             'msg': "获取成功",
#             'data':{
#                 'project': project_data,
#                 'total_amount': total_amount,
#                 'total': total,
#                 'generate_files': generate_files
#             }
#         }
#
#         return JsonResponse(res)
#
#     else:
#         res = {
#             'code': 0,
#             'msg': "不支持的请求方法"
#         }
#         return JsonResponse(res, status=405)

#获取项目详情
#获取投标任务（负责人）业绩信息
@timing_decorator
def api_get_project(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')

        if not _id or not _type:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['project', 'pmProject']:
            return JsonResponse({'code': 0, 'msg': '类型只能为project或pmProject'})

        # 格式化
        project_data = {}
        pm_name = ''

        # 查询公司业绩
        query = Q(bid=_id)

        if _type == 'project':
            bid_projects = ExBidProject.objects.filter(query).all().distinct()
        else:
            bid_projects = ExBidPmProject.objects.filter(query).select_related('project').all().distinct()

        if _type == 'pmProject':
            # 获取项目负责人情况
            # bid = ExBid.objects.filter(id=_id).select_related('createBy').first()
            # 获取项目负责人情况
            # bid = ExBid.objects.filter(id=_id).select_related('createBy').first()
            bid_team = ExBidTeam.objects.filter(bid=_id, position='项目负责人').select_related('account').first()
            # if not bid_team:
            #     return JsonResponse({'code': 0, 'msg': '不存在该id'})
            if bid_team:
                # 获取项目负责人信息
                pm = bid_team.account
                if pm:
                    pm_name = pm.name
                    professional = pm.professional
                else:
                    pm_name = ''
                    professional = ''

        # 总金额
        total_amount = Decimal('0.0')
        total = 0

        # 获取所有项目的id
        project_ids = set()
        for i, bid_project in enumerate(bid_projects, start=1):

            # 项目信息
            project = bid_project.project

            # 如果关联项目不存在，则跳过
            if not project:
                continue

            project_ids.add(project.id)

            # 项目金额
            contractAmount = project.contractAmount if project.contractAmount else 0

            # 对方联系人
            counterpartContact = project.counterpartContact if project.counterpartContact else ''

            # 对方电话
            counterpartPhone = project.counterpartPhone if project.counterpartPhone else ''

            # 总金额
            total_amount += Decimal(str(contractAmount))
            total += 1

            if _type == 'project':
                info = {
                    'id': project.id,
                    'name': project.name if project.name else '',
                    'address': project.address if project.address else '',
                    'counterpartName': project.counterpartName if project.counterpartName else '',
                    'counterpartContact': f'{counterpartContact}\n{counterpartPhone}',
                    'amount': contractAmount,
                    'desc': project.desc if project.desc else '',
                    'comment': project.comment if project.comment else '',
                }
            else:
                # 验收日期
                acceptanceDate = project.acceptanceDate
                if acceptanceDate:
                    year = f'{acceptanceDate.year}年'
                else:
                    year = ''

                info = {
                    'id': project.id,
                    'name': project.name if project.name else '',
                    'contractName': project.contractName if project.contractName else '',
                    'counterpartName': project.counterpartName if project.counterpartName else '',
                    'pm': pm_name,
                    'acceptanceYear': year,
                    'amount': contractAmount
                }

            project_data[project.id] = info

        # 获取该任务的生成文件
        query = Q(id=_id)

        # files = ExFile.objects.filter(query).all().distinct()

        bid = ExBid.objects.filter(query).select_related(_type).first()

        if not bid:
            return JsonResponse({'code': 0, 'msg': '没有该投标任务'})

        file = eval(f"bid.{_type}")

        generate_files = []

        if file:
            path = file.path
            generate_files.append({'name': os.path.split(path)[-1],
                                   'path': url_bs64_encode(path),
                                   'type': file.type})

        # 转为列表
        project_data = list(project_data.values())

        res = {
            'code': 1,
            'msg': "获取成功",
            'data':{
                'project': project_data,
                'total_amount': str(total_amount),
                'total': total,
                'generate_files': generate_files,
                'pm': pm_name
            }
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取（负责人）业绩详情
@timing_decorator
def api_get_project_info(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')

        if not _id or not _type:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['project', 'pmProject']:
            return JsonResponse({'code': 0, 'msg': '类型只能为project或pmProject'})

        # 查询公司业绩
        query = Q(id=_id)

        if _type == 'project':
            project = ExProject.objects.filter(query).\
                        select_related('companyChange','winBid','contractScan','contractTransfer','acceptanceCert').first()
        else:
            project = ExProject.objects.filter(query).\
                        select_related('PM','contractScan','acceptanceCert').first()

        if not project:
            return JsonResponse({'code': 0, 'msg': '该项目id不存在'})

        # 项目金额
        contractAmount = project.contractAmount if project.contractAmount else 0

        # 对方联系人
        counterpartContact = project.counterpartContact if project.counterpartContact else ''

        # 对方电话
        counterpartPhone = project.counterpartPhone if project.counterpartPhone else ''

        if _type == 'project':
            info = {
                'id': project.id,
                'name': project.name if project.name else '',
                'address': project.address if project.address else '',
                'counterpartName': project.counterpartName if project.counterpartName else '',
                'counterpartContact': f'{counterpartContact}\n{counterpartPhone}',
                'amount': contractAmount,
                'desc': project.desc if project.desc else '',
                'comment': project.comment if project.comment else '',
                'file': {}
            }

            for key in ['companyChange','winBid','contractScan','contractTransfer','acceptanceCert']:
                file = eval(f'project.{key}')
                if file:
                    path = file.path
                    info['file'][key] = {'path': url_bs64_encode(path), 'name': os.path.split(path)[-1],'type': file.type}
        else:
            # 验收日期
            acceptanceDate = project.acceptanceDate
            if acceptanceDate:
                year = f'{acceptanceDate.year}年'
            else:
                year = ''

            # 获取项目负责人情况
            # 获取项目负责人信息
            pm = project.PM
            if pm:
                pm_name = pm.name
                professional = pm.professional
            else:
                pm_name = ''
                professional = ''

            info = {
                'id': project.id,
                'name': project.name if project.name else '',
                'contractName': project.contractName if project.contractName else '',
                'counterpartName': project.counterpartName if project.counterpartName else '',
                'pm': pm_name,
                'acceptanceYear': year,
                'amount': contractAmount,
                'file': {}
            }

            for key in ['contractScan','acceptanceCert']:
                file = eval(f'project.{key}')
                if file:
                    path = file.path
                    info['file'][key] = {'path': url_bs64_encode(path), 'name': os.path.split(path)[-1],'type': file.type}

        # # 获取项目的附件
        # query = Q(objectId=_id)
        # query &= Q(objectType='project')
        #
        # files = ExFile.objects.filter(query).all().distinct()
        #
        # #中文映射
        # zh2en = {
        #     '中标通知书': "winBid",
        #     '合同扫描件': "contractScan",
        #     '合同转签协议': "transfer",
        #     '验收证书': "acceptance",
        #     '合同关键页': "contractKeyPage",
        # }
        #
        # for file in files:
        #     path = file.path
        #     info[zh2en[file.comment]] = {'path':url_bs64_encode(path),'name':os.path.split(path)[-1]}

        res = {
            'code': 1,
            'msg': "获取成功",
            'data':info
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取投标任务团队信息
@timing_decorator
def api_get_team(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')

        if not _id:
            return JsonResponse({'code': 0, 'msg': '未携带投标任务id'})

        # 格式化
        team_data = {}

        # 查询公司业绩
        query = Q(bid=_id)

        bid_accounts = ExBidTeam.objects.filter(query).select_related('account','account__dept').all().distinct()

        # 获取所有项目的id
        accounts = set()
        for i, bid_account in enumerate(bid_accounts, start=1):

            # 人员信息
            account = bid_account.account

            # 如果关联人员不存在，则跳过
            if not account:
                continue

            accounts.add(account)

            #部门
            dept = bid_account.account.dept
            if dept:
                dept = dept.name
            else:
                dept = ''

            info = {
                'account': account.account,
                'name': account.name,
                'position': bid_account.position,
                'degree': account.degree,
                'professional': account.professional if account.professional else '',
                'dept': dept,
                'workYears': account.workYears,
                'certs': []
            }

            team_data[account.account] = info

        # 获取资格证书
        query = Q(account__in=accounts)

        certs = ExTeamQualification.objects.filter(query).values('account', 'name').distinct()

        for cert in certs:
            team_data[cert['account']]['certs'].append(cert['name'])

        #拼接资格证书
        for key in team_data.keys():
            team_data[key]['certs'] = '\n'.join(team_data[key]['certs'])

        # 转为列表
        team_data = list(team_data.values())


        #获取投标任务的社保缴纳证明
        query = Q(bid=_id)
        team_bidSocialInsurances = ExBidSocialInsurance.objects.filter(query).select_related('file').distinct()
        bidSocialInsurances = []

        for obj in team_bidSocialInsurances:
            path = obj.file.path
            year = obj.year
            month = obj.month
            bidSocialInsurances.append([f"{year}{str(month).rjust(2, '0')}",
                                        {'path': url_bs64_encode(path),
                                         'name': os.path.split(path)[-1],
                                         'type': obj.file.type}])
        # 排序
        bidSocialInsurances = sorted(bidSocialInsurances, key=lambda x: x[0])
        bidSocialInsurances = [i[1] for i in bidSocialInsurances]

        # 获取该任务的生成文件
        query = Q(id=_id)

        bid = ExBid.objects.filter(query).select_related('team').first()

        if not bid:
            return JsonResponse({'code': 0, 'msg': '没有该投标任务'})

        file = bid.team
        generate_files = []

        if file:
            path = file.path
            generate_files.append({'name': os.path.split(path)[-1],
                                   'path': url_bs64_encode(path),
                                   'type': file.type})

        res = {
            'code': 1,
            'msg': "获取成功",
            'data':{
                'team': team_data,
                'bidSocialInsurances': bidSocialInsurances,
                'generate_files': generate_files
            }
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取团队详情
@timing_decorator
def api_get_team_info(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        account_input = request.GET.get('account')

        if not account_input:
            return JsonResponse({'code': 0, 'msg': '未携带用户名'})

        # 查询公司业绩
        query = Q(account=account_input)

        account = ExTeam.objects.filter(query).select_related('dept','company','degreeFile').first()

        if not account:
            return JsonResponse({'code': 0, 'msg': '不存在该人员'})

        account_info = {
            'account': account.account,
            'name': account.name,
            'degree': account.degree,
            'professional': account.professional if account.professional else '',
            'dept': account.dept.name if account.dept else '',
            'workYears': account.workYears,
            'company': account.company.name if account.company else '',
            'file': {'degreeFile':{'name':f"{account.university}-{account.degree}",
                                   "path":url_bs64_encode(account.degreeFile.path),
                                   'type': account.degreeFile.type}},
            'work': [],
            'projectContent': []
        }

        # 获取资格证书
        query = Q(account=account_input)

        certs = ExTeamQualification.objects.filter(query).select_related('file').distinct()

        if certs:
            account_info['file']['certs'] = []

        for cert in certs:
            account_info['file']['certs'].append({'name':cert.name,'path':url_bs64_encode(cert.file.path),'type': cert.file.type})

        # 获取个人社保缴纳证明
        query = Q(account=account_input)
        bidSocialInsurances = ExTeamSocialInsurance.objects.filter(query).select_related('file').distinct()

        socialInsurances = []

        for obj in bidSocialInsurances:
            path = obj.file.path
            year = obj.year
            month = obj.month
            socialInsurances.append([f"{year}{str(month).rjust(2, '0')}",
                                        {'path': url_bs64_encode(path),
                                         'name': os.path.split(path)[-1],
                                         'type': obj.file.type}])
        if socialInsurances:
            # 排序
            socialInsurances = sorted(socialInsurances, key=lambda x: x[0])
            socialInsurances = [i[1] for i in socialInsurances]
            account_info['file']['socialInsurances'] = socialInsurances

        # 工作经历
        query = Q(account=account_input)

        works = ExTeamWork.objects.filter(query).distinct()

        for work in works:
            begin = str(work.begin) if work.begin else ''
            end = str(work.end) if work.end else ''
            work_content = work.work if work.work else ''
            role = work.role if work.role else ''

            account_info['work'].append({'begin':begin,
                                         'end':end,
                                         'work':work_content,
                                         'role':role})

        # 以往参与项目情况
        query = Q(account=account_input)

        projects = ExTeamProject.objects.filter(query).distinct()

        for project in projects:
            year = str(project.year) if project.year else ''
            projectContent = str(project.projectContent) if project.projectContent else ''

            account_info['projectContent'].append({'year': year,
                                                   'projectContent': projectContent})

        res = {
            'code': 1,
            'msg': "获取成功",
            'data':account_info
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#生成公司业绩
@timing_decorator
def api_generate_project(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')

        if not _id or not _type:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['project', 'pmProject']:
            return JsonResponse({'code': 0, 'msg': '类型只能为project或pmProject'})

        # 格式化
        project_data = {}

        # 额外信息
        extra_data = {}

        if _type == 'pmProject':
            bid_team = ExBidTeam.objects.filter(bid=_id, position='项目负责人').select_related('account','account__IDCardFront',
                                                                                             'account__IDCardBack','account__degreeFile').first()
            # if not bid_team:
            #     return JsonResponse({'code': 0, 'msg': '不存在该id'})
            pm_name = ''
            professional = ''
            sex = ''
            degree = ''
            position = ''
            if bid_team:
                # 获取项目负责人信息
                pm = bid_team.account
                if pm:
                    pm_name = pm.name
                    professional = pm.professional
                    sex = pm.sex
                    degree = pm.degree
                    position = bid_team.position

            # 获取项目负责人信息
            extra_data['pm'] = pm_name
            extra_data['professional'] = professional
            extra_data['sex'] = sex
            extra_data['degree'] = degree
            extra_data['position'] = position

            # 项目负责人其他信息
            extra_data['pm_info'] = {}

            # 身份证
            extra_data['pm_info']['身份证'] = []
            if bid_team.account.IDCardBack:
                extra_data['pm_info']['身份证'].append(os.path.join(ROOT_FOLDER, bid_team.account.IDCardBack.path))
            if bid_team.account.IDCardFront:
                extra_data['pm_info']['身份证'].append(os.path.join(ROOT_FOLDER, bid_team.account.IDCardFront.path))

            #学历证书
            extra_data['pm_info']['学历证书'] = []
            if bid_team.account.degreeFile:
                extra_data['pm_info']['学历证书'].append(os.path.join(ROOT_FOLDER, bid_team.account.degreeFile.path))

            #资格证书
            extra_data['pm_info']['资格证书'] = {}
            quas = ExTeamQualification.objects.filter(account=bid_team.account.account).select_related("file").distinct()
            for qua in quas:
                number = f"：{qua.number}" if qua.number else ""
                title = f"{qua.name}{number}"
                if qua.file.path:
                    extra_data['pm_info']['资格证书'][title] = [os.path.join(ROOT_FOLDER, qua.file.path)]

            #社保证明
            extra_data['pm_info']['社保证明'] = []
            insurances = ExTeamSocialInsurance.objects.filter(account=bid_team.account.account).select_related("file").distinct()\
                                                                                        .order_by('-year', '-month')
            for insurance in insurances[:3]:
                if insurance.file.path:
                    extra_data['pm_info']['社保证明'].append(os.path.join(ROOT_FOLDER, insurance.file.path))

            # with open('测试.json', 'w') as f:
            #     json.dump(extra_data, f, ensure_ascii=False)

        #查询公司业绩
        query = Q(bid=_id)

        if _type == 'project':
            bid_projects = ExBidProject.objects.filter(query).select_related('project','project__companyChange',
                'project__winBid','project__contractScan','project__contractTransfer','project__acceptanceCert').all().distinct()
        else:
            bid_projects = ExBidPmProject.objects.filter(query).select_related('project','project__contractScan',
                                                                               'project__acceptanceCert').all().distinct()

        #总金额
        total_amount = Decimal('0.0')

        #获取所有项目的id
        project_ids = set()
        for i,bid_project in enumerate(bid_projects, start=1):

            #项目信息
            project = bid_project.project

            #如果关联项目不存在，则跳过
            if not project:
                continue

            project_ids.add(project.id)

            #项目金额
            contractAmount = project.contractAmount if project.contractAmount else 0

            #对方联系人
            counterpartContact = project.counterpartContact if project.counterpartContact else ''

            #对方电话
            counterpartPhone = project.counterpartPhone if project.counterpartPhone else ''

            #总金额
            total_amount += Decimal(str(contractAmount))

            if _type == 'project':
                info = {
                    '序号':i,
                    '项目名称': project.name if project.name else '',
                    '项目地点': project.address if project.address else '',
                    '合同(服务)对方名称': project.counterpartName if project.counterpartName else '',
                    '对方联系人及电话': f'{counterpartContact}\n{counterpartPhone}',
                    '合同金额(万元)': contractAmount,
                    '项目描述': project.desc if project.desc else '',
                    '备注': project.comment if project.comment else '',
                }

                #附件信息
                en2zh = {
                    "companyChange":"企业名称变更备案",
                    "winBid":"中标通知书",
                    "contractScan":"合同扫描件",
                    "contractTransfer":"合同转签协议",
                    "acceptanceCert":"验收证书",
                    "invoice": "发票"
                }
                for key in ['companyChange', 'winBid', 'contractScan', 'contractTransfer', 'acceptanceCert', 'invoice']:
                    file = eval(f'project.{key}')
                    if file:
                        info[en2zh[key]] = os.path.join(ROOT_FOLDER, file.path)

            else:
                # 验收日期
                acceptanceDate = project.acceptanceDate
                if acceptanceDate:
                    year = f'{acceptanceDate.year}年'
                else:
                    year = ''

                info = {
                    '序号': i,
                    '项目名称': project.name if project.name else '',
                    '合同名称': project.contractName if project.contractName else '',
                    '甲方单位': project.counterpartName if project.counterpartName else '',
                    '乙方项目负责人': extra_data['pm'],
                    '项目验收年份': year,
                    '项目金额（万元）': contractAmount
                }

                # 附件信息
                en2zh = {
                    "contractScan": "合同关键页",
                    "acceptanceCert": "验收证书"
                }
                for key in ['contractScan', 'acceptanceCert']:
                    file = eval(f'project.{key}')
                    if file:
                        info[en2zh[key]] = os.path.join(ROOT_FOLDER, file.path)

            project_data[project.id] = info

        # 总金额
        extra_data['total_amount'] = str(total_amount)


        #获取项目的附件
        # query = Q(objectId__in=project_ids)
        # query &= Q(objectType='project')
        #
        # files = ExFile.objects.filter(query).all().distinct()
        #
        # for file in files:
        #     project_data[file.objectId][file.comment] = file.path

        #转为列表
        project_data = list(project_data.values())

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        # 文件存储位置
        save_folder = os.path.join(BIG_SAVE_FOLDER, _type)
        #映射
        en2zh = {
            'project': '业绩',
            "pmProject": '负责人业绩'
        }
        now = time.strftime('%Y%m%d_%H%M%S',time.localtime(time.time()))
        filename = f"{en2zh[_type]}生成_{now}.docx"
        save_path = os.path.join(save_folder, filename)
        if not os.path.isdir(save_folder):
            os.makedirs(save_folder)

        # 放到内存中
        global_thread[_type][task_id] = {'save_path': save_path, 'bid':_id}

        if _type == 'project':
            job = project_thread(project_data, extra_data, save_path, task_id, _id)
        else:
            job = pmProject_thread(project_data, extra_data, save_path, task_id, _id)
        job.start()

        # 放入到内存中
        global_thread[_type][task_id]['job'] = job

        res = {
            'code': 1,
            'task_id': task_id,
            'msg': "任务创建成功"
        }

        return JsonResponse(res)
    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#生成团队管理
@timing_decorator
def api_generate_team(request):
    if request.method == 'GET':

        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')

        # 格式化
        team_info = {}

        #查询项目负责人数量
        bid_pm = ExBidTeam.objects.filter(bid=_id, position='项目负责人').distinct()
        if not bid_pm:
            return JsonResponse({'code': 0, 'msg': '没有项目负责人，请添加“项目负责人”职务'})
        elif len(bid_pm) != 1:
            return JsonResponse({'code': 0, 'msg': '有多个项目负责人，请只留一个'})

        #查询相关人员
        query = Q(bid=_id)

        bid_team = ExBidTeam.objects.filter(query).select_related('account', 'account__degreeFile').distinct()

        #所有用户
        accounts = set()

        #id对应account
        degree_id2account = {}
        for i, bid_account in enumerate(bid_team, start=1):
            account = bid_account.account
            # 如果没有这个人，直接跳过
            if not account:
                continue

            accounts.add(account.account)
            degree_id2account[account.id] = account.account

            if bid_account.position == '项目负责人':
                workYears = f'{account.workYears}年以上项目管理经验'
            else:
                workYears = f'{account.workYears}年以上'

            #获取学位证书
            file = bid_account.account.degreeFile
            if file:
                degreeFile = [os.path.join(ROOT_FOLDER, file.path)]
            else:
                degreeFile = []

            #用户信息
            team_info[account.account] = {
                "职责分工": bid_account.position,
                "行政办公项目年限（年）": workYears,
                "主要持有证书": [],
                "姓名": account.name,
                "学历": account.degree,
                "本项目中职务": bid_account.position,
                "主要资格证书名称及编号": [],
                "主要工作经历": [],
                "以往参与项目情况": [],
                "学历截图": degreeFile,
                "证书截图": []
            }

        #团队证书
        # id对应account
        qua_id2account = {}
        query = Q(account__in=accounts)

        quas = ExTeamQualification.objects.filter(query).values('id', 'account', 'name', 'number', 'level', 'file__path').distinct()

        for qua in quas:
            if qua['level']:
                team_info[qua['account']]['主要持有证书'].append(f"{qua['name']}({qua['level']})")
            else:
                team_info[qua['account']]['主要持有证书'].append(f"{qua['name']}")
            team_info[qua['account']]['主要资格证书名称及编号'].append(f"{qua['name']}： {qua['number']}")
            team_info[qua['account']]['证书截图'].append(os.path.join(ROOT_FOLDER, qua['file__path']))

            qua_id2account[qua['id']] = qua['account']

        #工作经历
        query = Q(account__in=accounts)

        works = ExTeamWork.objects.filter(query).distinct()

        for work in works:
            if work.end:
                if work.end.isdigit():
                    year = f"{work.begin}-{work.end}年，"
                else:
                    year = f"{work.begin}年-{work.end}，"
            else:
                if work.begin:
                    year = f"{work.begin}年，"
                else:
                    year = ''

            if work.role:
                work_content = f"{year}作为{work.role}，{work.work}。"
            else:
                work_content = f"{year}{work.work}。"
            team_info[work.account]['主要工作经历'].append(work_content)

        # 以往参与项目情况
        query = Q(account__in=accounts)

        projects = ExTeamProject.objects.filter(query).distinct()

        for i, project in enumerate(projects,start=1):
            team_info[project.account]['以往参与项目情况'].append(f"{i}、{project.year}年，{project.projectContent}。")

        #获取团队的社保缴纳证明
        query = Q(bid=_id)
        bidSocialInsurances = ExBidSocialInsurance.objects.filter(query).select_related('file').distinct()
        team_bidSocialInsurances = []

        for obj in bidSocialInsurances:
            path = os.path.join(ROOT_FOLDER, obj.file.path)
            year = obj.year
            month = obj.month
            team_bidSocialInsurances.append([f"{year}{str(month).rjust(2,'0')}",
                                            f"{year}年{month}月社保缴纳证明",
                                            path])

        #排序
        team_bidSocialInsurances = sorted(team_bidSocialInsurances, key=lambda x:x[0])

        #获取项目的附件
        # query1 = Q(objectId__in=degree_id2account.keys()) & Q(objectType='teamDegreeCertificate')
        # query2 = Q(objectId__in=qua_id2account.keys()) & Q(objectType='teamQualification')
        # query = query1 | query2
        #
        # files = ExFile.objects.filter(query).all().distinct()
        #
        # for file in files:
        #     if file.objectType == 'teamDegreeCertificate':
        #         team_info[degree_id2account[file.objectId]]['学历截图'].append(file.path)
        #     else:
        #         team_info[qua_id2account[file.objectId]]['证书截图'].append(file.path)
            # print(file.objectType, file.objectId, file.path)

        #转为列表
        team_info = list(team_info.values())

        # generate_team(team_info[:10], '/media/977GB/wcj_work/投标文件生成/团队生成.docx')
        _type = 'team'
        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        # 文件存储位置
        save_folder = os.path.join(BIG_SAVE_FOLDER, _type)
        now = time.strftime('%Y%m%d_%H%M%S',time.localtime(time.time()))
        filename = f"团队生成_{now}.docx"
        save_path = os.path.join(save_folder, filename)
        if not os.path.isdir(save_folder):
            os.makedirs(save_folder)

        # 放到内存中
        global_thread[_type][task_id] = {'save_path': save_path, 'bid':_id}

        job = team_thread(team_info,team_bidSocialInsurances, save_path, task_id, _id)
        job.start()

        # 放入到内存中
        global_thread[_type][task_id]['job'] = job

        res = {
            'code': 1,
            'task_id': task_id,
            'msg': "任务创建成功"
        }

        return JsonResponse(res)
    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#数据库执行
def pymysql_import(connection, sql, values):
    # 连接数据库
    with connection.cursor() as cursor:

        # 执行 SQL 语句
        cursor.execute(sql, values)

        # 提交事务
        connection.commit()

        return cursor.lastrowid

def get_numeric_part(file):
    """提取文件名中的数字部分"""
    match = re.search(r'\d+', file.name)
    return int(match.group()) if match else 0

def sorted_files_by_numeric_prefix(files):
    """按照文件名中的数字顺序排序文件夹中的文件"""
    files.sort(key=get_numeric_part)
    return files

#上传商务/技术文件
@timing_decorator
def api_upload_template(request):

    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 投标任务id
        _id = request.POST.get('id')
        # 类型
        _type = request.POST.get('type')
        #委托代理人
        agent_account = request.POST.get('agent')
        # 上传的文件
        files = request.FILES.getlist('files')

        print('_id: ', [_id])

        if not _type or not _id or not agent_account or not files:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或代理人或文件'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        #查询委托代理人
        agent = ExLegalAgents.objects.filter(account=agent_account).first()

        #更新投标任务中的委托代理人
        ExBid.objects.filter(id=_id).update(agent=agent)

        # #查询投标任务
        bid = ExBid.objects.filter(id=_id).first()

        #对文件排序
        files = sorted_files_by_numeric_prefix(files)

        #保存文件
        for i, file in enumerate(files):
            # # 如果有文件，先存储
            # 文件存储位置
            save_folder = os.path.join(BIG_SAVE_FOLDER, 'bid_generate', str(_id),_type,'template')
            if not os.path.isdir(save_folder):
                os.makedirs(save_folder)

            # now = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))

            # 保存文件
            # path = os.path.join(save_folder, f"{now}_{file.name}")
            path = os.path.join(save_folder, file.name)

            # 对每个上传的文件进行处理，例如保存文件
            with open(path, 'wb+') as destination:
                for chunk in file.chunks():
                    destination.write(chunk)

            #插入文件
            file_type = 'pdf' if 'pdf' in os.path.splitext(file.name)[-1] else 'img'
            file_obj = ExFile.objects.create(path=path.replace(ROOT_FOLDER, ''),
                                             type=file_type,
                                             uploadDate=time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

            #插入数据库
            ExBidCatalog.objects.create(uuid=str(uuid.uuid4()).replace('-',''),
                                        bid=bid,
                                        type=_type,
                                        name=os.path.splitext(file.name)[0],
                                        sourceFile=file_obj,
                                        order=i)

        # 更新状态为上传
        if _type == 'tech':
            ExBid.objects.filter(id=_id).update(techStatus='upload')
        elif _type == 'business':
            ExBid.objects.filter(id=_id).update(businessStatus='upload')

        res = {
            'code': 1,
            'msg': "上传成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#生成文件目录
@timing_decorator
def api_generate_catalog(request):

    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取内容
        _id = data.get('id')
        _type = data.get('type')

        if not _type or not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        # 放到内存中
        global_thread[_type][task_id] = {}

        #生成目录
        job = tech_catalog_generate_thread('/media/977GB/virtualbox/24_技术要求_标的1：业务系统2024年第五批采购项目-标.docx', _id, task_id)
        job.start()

        # 放入到内存中
        global_thread[_type][task_id]['job'] = job

        res = {
            'code': 1,
            'task_id': task_id,
            'msg': "任务创建成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#对数据进行排序，父节点在前，并且顺序高的在前
def sorted_catalog(path, order):

    if path:
        path_len = len(path.split(','))
    else:
        path_len = 0

    return (path_len, order)

#组装为树结构字典
def catalog_to_tree(catalogs, need_content=False):

    # 初始化树结构
    tree = {}

    # 遍历路径列表，构建树结构
    for catalog in catalogs:
        # 移除路径两端的逗号，并分割成步骤列表
        steps = catalog['path'].strip(',').split(',')

        #将当前id也加入路径
        steps.append(str(catalog['id']))

        # 递归地构建或更新树结构
        current_level = tree
        for step in steps:
            if step:  # 确保步骤不为空
                if step not in current_level:
                    if need_content:
                        current_level[step] = {'key': catalog['uuid'],
                                               'title': catalog['name'],
                                               'content': '\n'.join([catalog['originalContent'],catalog['genarateContent']]),
                                               'path': catalog['path'],
                                               'children': {}}
                    else:
                        current_level[step] = {'key': catalog['uuid'],
                                               'title': catalog['name'],
                                               "generateFlag": catalog['generateFlag'],
                                               'children': {}}


                current_level = current_level[step]['children']
    return tree

#将树结构字典转为列表
def tree_dict2list(tree):

    #更新状态和日期
    def update_parent_status(node):
        # 获取子节点字典
        children = node.get('children', {})

        # 如果没有子节点，则不更新状态
        if not children:

            # 将字段转为列表
            node['children'] = list(node['children'].values())
            return

        # 遍历所有子节点
        for child_node in children.values():
            # 递归更新子节点的状态
            update_parent_status(child_node)

        #将字段转为列表
        node['children'] = list(node['children'].values())

    #更新状态和日期
    for _, module in tree.items():
        update_parent_status(module)

    return list(tree.values())

#获取文件目录
@timing_decorator
def api_get_catalog(request):

    if request.method == 'GET':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})


        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')

        if not _type or not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        if _type == 'tech':
            #从数据库获取文件
            query = Q(bid=_id, type=_type)
            catalogs = ExBidCatalog.objects.filter(query).values('id', 'uuid', 'name', 'path', 'order', 'generateFlag').distinct()

            catalogs = sorted(catalogs, key=lambda x : sorted_catalog(x['path'], x['order']))

            # 获取字典树结构
            catalogs_dict_tree = catalog_to_tree(catalogs)

            # 将树结构字典转为列表
            catalogs_list_tree = tree_dict2list(catalogs_dict_tree)

            return JsonResponse({'code': 1, 'msg': '获取成功', 'data': catalogs_list_tree})
        elif _type == 'business':

            # directory_path = 'E:/code_why/4sql自动生成/本地RAG-dawate-投标生成/商务文件生成/data/1'  # 替换为您的文件夹路径
            # directory_path = 'E:/code_why/4sql自动生成/本地RAG-dawate-投标生成/商务文件生成/项目3'  # 替换为您的文件夹路径
            # sorted_files = sorted_files_by_numeric_prefix(directory_path)
            # # bid = ExBid.objects.filter(id=_id).first()
            # for i, file in enumerate(sorted_files):
            #     print(file)
            #     ExBidCatalog.objects.create(uuid=str(uuid.uuid4()).replace('-',''),
            #                                 bid=bid,
            #                                 type=_type,
            #                                 name=os.path.splitext(file)[0],
            #                                 generateFlag='1',
            #                                 originalContent='',
            #                                 path=os.path.join(directory_path, file),
            #                                 order=i)

            # 查找投标任务
            # bid = ExBid.objects.filter(id=_id).first()
            catalogs = ExBidCatalog.objects.filter(bid=_id, type=_type).values('id', 'uuid', 'name', 'order').distinct()

            catalogs = sorted(catalogs, key=lambda x: x['order'])
            data = []
            for catalog in catalogs:
                d_i = {
                    "key": catalog['uuid'],
                    "title": catalog['name'],
                    "generateFlag": "1",
                    "children": []}
                data.append(d_i)

            res = {
                'code': 1,
                'data': data,
                'msg': "获取成功"
            }
            return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#修改目录是否需要生成内容
@timing_decorator
def api_catalog_generateFlag_modify(request):

    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        #获取内容
        _id = data.get('id')
        _type = data.get('type')
        current_uuid = data.get('current_uuid')
        generate_flag = data.get('generate_flag')

        if not _type or not _id or not current_uuid or not generate_flag:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或目录uuid或生成状态'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        if generate_flag not in ['0', '1']:
            return JsonResponse({'code': 0, 'msg': '生成字段只能为"0"或"1"'})

        #修改数据库字段
        query = Q(bid=_id, type=_type, uuid=current_uuid)
        ExBidCatalog.objects.filter(query).update(generateFlag=generate_flag)

        return JsonResponse({'code': 1, 'msg': '修改成功'})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#目录节点新增
@timing_decorator
def api_catalog_add(request):
    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取内容
        _id = data.get('id')
        _type = data.get('type')
        parent_uuid = data.get('parent_uuid')
        name = data.get('name')
        content = data.get('content')

        #如果没有content，赋值为""
        if not content:
            content = ""

        if not _type or not _id or not parent_uuid or not name:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或uuid或标题名称'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        #获取父节点条目
        query = Q(bid=_id, type=_type, uuid=parent_uuid)
        parent_node = ExBidCatalog.objects.filter(query).values('id', 'path').first()

        if not parent_node:
            return JsonResponse({'code': 0, 'msg': '不存在该节点'})

        #获取该父节点下的所有子节点
        if parent_node['path']:
            parent_path = f"{parent_node['path']},{parent_node['id']}"
        else:
            parent_path = str(parent_node['id'])
        query = Q(bid=_id, type=_type, path=parent_path)

        child_nodes = ExBidCatalog.objects.filter(query).values('id', 'order').distinct()

        #默认顺序最靠后
        order = -1
        for child_node in child_nodes:
            if child_node['order'] > order:
                order = child_node['order']

        #获取投标任务对象
        bid = ExBid.objects.filter(id=_id).first()

        #新增目录
        ExBidCatalog.objects.create(bid=bid,
                                    uuid=str(uuid.uuid4()).replace('-',''),
                                    type=_type,
                                    name=name,
                                    path=parent_path,
                                    order=order+1,
                                    generateFlag='1',
                                    originalContent=content)

        return JsonResponse({'code': 1, 'msg': '新增成功'})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#目录节点删除
@timing_decorator
def api_catalog_delete(request):

    if request.method == 'DELETE':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')
        current_uuid = request.GET.get('current_uuid')

        if not _type or not _id or not current_uuid:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或uuid'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        #删除条目
        query = Q(bid=_id, type=_type, uuid=current_uuid)
        ExBidCatalog.objects.filter(query).delete()

        return JsonResponse({'code': 1, 'msg': '删除成功'})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#目录节点移动
@timing_decorator
def api_catalog_move(request):

    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取内容
        _id = data.get('id')
        _type = data.get('type')
        parent_uuid = data.get('parent_uuid')   #父节点
        front_uuid = data.get('front_uuid')     #前置节点
        moved_uuid = data.get('moved_uuid')     #被移动节点

        if not _type or not _id or not parent_uuid or not moved_uuid:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或父节点或被移动节点'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        #获取父节点条目
        query = Q(bid=_id, type=_type, uuid=parent_uuid)
        parent_node = ExBidCatalog.objects.filter(query).values('id', 'path').first()

        if not parent_node:
            return JsonResponse({'code': 0, 'msg': '不存在该节点'})

        #被移动节点的父路径
        if parent_node['path']:
            parent_path = f"{parent_node['path']},{parent_node['id']}"
        else:
            parent_path = str(parent_node['id'])

        #获取前置节点的order，如果front_uuid为空，则代表order为0
        if front_uuid:
            query = Q(bid=_id, type=_type, uuid=front_uuid)
            front_node = ExBidCatalog.objects.filter(query).values('id', 'order').first()

            # 被移动节点的order为前置order+1
            order = front_node['order'] + 1
        else:
            order = 0

        #将该父路径下≥被移动节点的order都+1
        query = Q(bid=_id, type=_type, path=parent_path, order__gte=order)
        ExBidCatalog.objects.filter(query).update(order=F('order') + 1)

        #更新被移动节点的父路径和order
        query = Q(bid=_id, type=_type, uuid=moved_uuid)

        ExBidCatalog.objects.filter(query).update(path=parent_path,
                                                  order=order)

        return JsonResponse({'code': 1, 'msg': '移动成功'})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#商务、技术文件内容生成
@timing_decorator
def api_bid_all_generate(request):
    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取内容
        _id = data.get('id')
        _type = data.get('type')

        if not _type or not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        # 如果没有该任务，则添加
        if _type not in global_thread.keys():
            global_thread[_type] = {}
        global_thread[_type][task_id] = {}

        # 获取队列中的agent
        if AGENT_QUEUE.empty():
            global_thread[_type][task_id]['status'] = '排队中'
            status = 'queue'
        else:
            global_thread[_type][task_id]['agent'] = AGENT_QUEUE.get()
            global_thread[_type][task_id]['status'] = '进行中'
            status = 'doing'

        #从数据库获取文件
        query = Q(bid=_id, type=_type)
        if _type == 'tech':
            catalogs = ExBidCatalog.objects.filter(query).values('id', 'uuid', 'name', 'path', 'order', 'generateFlag', 'originalContent').distinct()
            # 更新状态为进行中
            ExBid.objects.filter(id=_id).update(techStatus=status)
        elif _type == 'business':
            catalogs = ExBidCatalog.objects.filter(query).select_related('sourceFile','generateFile').distinct()

            #判断是否已经生成
            # judge = True
            # for catalog in catalogs:
            #     if not catalog.generateFile:
            #         judge = False
            # if judge:
            #     return JsonResponse({'code': 0, 'msg': '商务文件已经生成过了'})
            # else:
            # 更新状态为进行中
            ExBid.objects.filter(id=_id).update(businessStatus=status)

        # 排序
        # catalogs = sorted(catalogs, key=lambda x : sorted_catalog(x['path'], x['order']))

        # 放到内存中
        # global_thread[_type][task_id] = {}

        ##根据标题生成内容
        job = business_tech_generate_thread(_type, catalogs, _id, request.session['account'], task_id)
        job.start()

        # 放入到内存中
        global_thread[_type][task_id]['job'] = job

        res = {
            'code': 1,
            'task_id': task_id,
            'msg': "任务创建成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#商务、技术文件单个章节内容生成
@timing_decorator
def api_bid_single_generate(request):

    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取内容
        _id = data.get('id')
        _type = data.get('type')
        catalog_uuid = data.get('catalog_uuid')   #父节点

        if not _type or not _id or not catalog_uuid:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型或节点key'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        # 生成唯一任务id
        task_id = str(shortuuid.uuid())
        if task_id not in global_thread[_type].keys():
            task_id = str(shortuuid.uuid())
            while task_id in global_thread[_type].keys():
                task_id = str(shortuuid.uuid())

        # 放到内存中
        # global_thread[_type][task_id] = {}

        # 如果没有该任务，则添加
        if _type not in global_thread.keys():
            global_thread[_type] = {}
        global_thread[_type][task_id] = {}

        # 获取队列中的agent
        if AGENT_QUEUE.empty():
            global_thread[_type][task_id]['status'] = '排队中'
            status = 'queue'
        else:
            global_thread[_type][task_id]['agent'] = AGENT_QUEUE.get()
            global_thread[_type][task_id]['status'] = '进行中'
            status = 'doing'

        if _type == 'tech':
            #获取该uuid对应的条目
            query = Q(bid=_id, type=_type, uuid=catalog_uuid)
            catalog = ExBidCatalog.objects.filter(query).values('id', 'name', 'path').first()

            if not catalog:
                return JsonResponse({'code': 0, 'msg': '不存在该目录'})

            #父节点的id，目录不要
            parent_ids = catalog['path'].split(',')[1:]
            #获取该目录对应的父节点的名称
            query = Q(bid=_id, type=_type, id__in=parent_ids)
            parent_catalogs = ExBidCatalog.objects.filter(query).values('id', 'name').distinct()

            #id和名称对应
            id2title = {}
            for parent_catalog in parent_catalogs:
                id2title[str(parent_catalog['id'])] = parent_catalog['name']

            # 将目录拼接起来
            title_list = [id2title[parent_id] for parent_id in parent_ids]
            title_list.append(catalog['name'])
            title = ','.join(title_list)

            ##根据标题生成内容
            job = tech_single_generate_thread(title, catalog['id'], task_id, _type)
            job.start()

        elif _type=='business':

            # 获取该uuid对应的条目
            query = Q(bid=_id, type=_type, uuid=catalog_uuid)

            catalogs = ExBidCatalog.objects.filter(query).select_related('sourceFile', 'generateFile').distinct()

            # 更新状态为进行中
            ExBid.objects.filter(id=_id).update(businessStatus=status)


            ##根据标题生成内容
            job = business_tech_generate_thread(_type, catalogs, _id, request.session['account'], task_id)
            job.start()

        # 放入到内存中
        global_thread[_type][task_id]['job'] = job

        res = {
            'code': 1,
            'task_id': task_id,
            'msg': "任务创建成功"
        }

        return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)


#将树结构技术文件转为html文本
def tree2html(catalogs, html_list=[]):
    if len(catalogs) > 0:
        for catalog in catalogs:

            #添加标题
            title_level = f"h{len(catalog['path'].split(','))}"
            html_list.append(f'<{title_level} id="{catalog["key"]}">{catalog["title"]}</{title_level}>')

            # 如果有内容添加内容
            if catalog['content']:
                # 通过\n切分，每段单独放置
                paras = catalog['content'].split('\n')
                for para in paras:
                    html_list.append(f'<p>{para}</p>')

            #迭代
            html_list = tree2html(catalog['children'].values(), html_list)

    return html_list

#获取技术文件生成的内容
@timing_decorator
def api_get_bid_content(request):

    if request.method == 'GET':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})


        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')
        catalog_uuid = request.GET.get('uuid')

        if not _type or not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        if _type == 'business' and not catalog_uuid:
            return JsonResponse({'code': 0, 'msg': '商务文件内容获取必须携带uuid'})

        if _type == 'tech':
            #从数据库获取文件
            query = Q(bid=_id, type=_type)
            catalogs = ExBidCatalog.objects.filter(query).values('id', 'uuid', 'name', 'path', 'order', 'originalContent', 'genarateContent').distinct()

            # 排序
            catalogs = sorted(catalogs, key=lambda x : sorted_catalog(x['path'], x['order']))

            #梳理成json树结构
            tree = catalog_to_tree(catalogs, need_content=True)

            #整理为html
            html_list = tree2html(tree.values(), html_list=[])

            body_html = '\n'.join(html_list)
            # 将html列表组装为html
            html = f'''<!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>技术文件生成内容</title>
            <style>
            p {{
            text-indent: 2em; /* 设置首行缩进为2em */
            }}
            </style>
            </head>
            <body>
            {body_html}
            </body>
            </html>'''

            return HttpResponse(html)
        elif _type == 'business':
            catalog = ExBidCatalog.objects.filter(bid=_id, type=_type, uuid=catalog_uuid).select_related('generateFile').first()

            if not catalog:
                return JsonResponse({"code":0, "msg":"没有该目录"})

            if not catalog.generateFile:
                return JsonResponse({"code": 0, "msg": "该目录没有生成文件"})

            res = {
                'code': 1,
                'msg': "成功",
                'path': url_bs64_encode(catalog.generateFile.path)
            }
            return JsonResponse(res)

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#将生成的内容转为word
@timing_decorator
def api_bid_generate_word(request):

    if request.method == 'GET':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        _id = request.GET.get('id')
        _type = request.GET.get('type')

        if not _type or not _id:
            return JsonResponse({'code': 0, 'msg': '未携带id或类型'})

        if _type not in ['business', 'tech']:
            return JsonResponse({'code': 0, 'msg': '类型只能为business或tech'})

        #从数据库获取文件
        query = Q(bid=_id, type=_type)
        catalogs = ExBidCatalog.objects.filter(query).values('id', 'uuid', 'name', 'path', 'order', 'originalContent', 'genarateContent').distinct()

        # 排序
        catalogs = sorted(catalogs, key=lambda x : sorted_catalog(x['path'], x['order']))

        #梳理成json树结构
        tree = catalog_to_tree(catalogs, need_content=True)

        #不要目录，只要内容
        tree_values = list(tree.values())[0]['children']
        # 生成word
        doc = tree2word(tree_values.values())

        save_folder = os.path.join(BIG_SAVE_FOLDER, f"bid_generate/{_type}")

        #如果没有文件夹，则创建
        if not os.path.isdir(save_folder):
            os.makedirs(save_folder)

        # 当前时间
        current = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
        filename = f"{_id}_{current}.docx"

        save_path = os.path.join(save_folder, filename)

        # 保存文件
        doc.save(save_path)

        return JsonResponse({'code': 1, 'msg': '生成成功', 'path':url_bs64_encode(save_path.replace(ROOT_FOLDER, ''))})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#获取任务生成的状态
@timing_decorator
def api_generate_status(request):
    if request.method == 'GET':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        task_id = request.GET.get('task_id')
        _type = request.GET.get('type')

        if not task_id or not _type:
            return JsonResponse({'code': 0, 'msg': '未携带任务id或任务类型'})

        if _type not in ['project', 'pmProject', 'team', 'tech', 'business']:
            return JsonResponse({'code': 0, 'msg': '类型只能为project、pmProject、team、tech、business'})

        if task_id not in global_thread[_type].keys():
            return JsonResponse({'code': 0, 'msg': '生成任务不存在'})

        if global_thread[_type][task_id]['job'].is_alive():
            return JsonResponse({'code': 0, 'msg': '生成任务进行中...'})

        if _type in ['tech']:
            return JsonResponse({'code': 1, 'msg': '生成任务完成'})
        elif _type in ['business']:
            return JsonResponse({'code': 1, 'msg': '生成任务完成'})
        else:
            file_path = global_thread[_type][task_id]['save_path']
            if not os.path.isfile(file_path):
                global_thread[_type].pop(task_id)
                return JsonResponse({'code': 0, 'msg': '生成任务失败'})

            #成功后删除任务
            bid = global_thread[_type][task_id]['bid']
            global_thread[_type].pop(task_id)

            # # 建立数据库连接
            # try:
            #     # pymysql连接数据
            #     connection = pymysql.connect(**PYMYSQL_CONF)
            # except:
            #     print(traceback.format_exc())
            #     return JsonResponse({'code': 0, 'msg': "无法连接到数据库"})
            #
            # #插入数据库
            # try:
            #     # 插入上传文件
            #     sql = """
            #     insert into ex_file (path,type,comment,uploadDate)
            #     VALUES (%s, %s, %s, %s)
            #     """
            #     timenow = time.strftime('%Y-%m-%d',time.localtime(time.time()))
            #     values = (file_path.replace(ROOT_FOLDER, ''), 'docx', _type, timenow)
            #     # 执行插入
            #     file_id = pymysql_import(connection,sql,values)
            #
            #     #更新bid附件信息
            #     sql = f"""
            #     update ex_bid set {_type}=%s where id=%s
            #     """
            #     values = (file_id, int(bid))
            #     pymysql_import(connection, sql, values)
            #
            # except pymysql.MySQLError as e:
            #     print(f"Error: {e}")
            #     # 发生错误时回滚
            #     connection.rollback()
            #     return JsonResponse({'code': 0, 'msg': "数据库数据插入错误"})
            # finally:
            #     # 关闭数据库连接
            #     connection.close()

            return JsonResponse({'code': 1, 'msg': "生成任务完成", 'path':url_bs64_encode(file_path.replace(ROOT_FOLDER, ''))})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)


#获取文件
@timing_decorator
def api_get_file(request):

    if request.method == 'GET':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        path = request.GET.get('path')

        if not path:
            return JsonResponse({'code': 0, 'msg': '未携带文件路径'})

        try:
            path = url_bs64_decode(path)
        except:
            return JsonResponse({'code': 0, 'msg': '路径编码有误'})

        #路径拼接
        path = os.path.join(ROOT_FOLDER, path)

        if not os.path.isfile(path):
            return JsonResponse({'code': 0, 'msg': '该文件不存在'})

        # 获取文件大小
        file_size = os.path.getsize(path)

        # 打开文件并准备分块读取
        def file_iterator(file_path, chunk_size=8192):
            with open(file_path, 'rb') as f:
                while True:
                    chunk = f.read(chunk_size)
                    if chunk:
                        yield chunk
                    else:
                        break

        # 设置文件名为中文，并进行URL编码
        encoded_filename = iri_to_uri(os.path.split(path)[-1])

        response = StreamingHttpResponse(file_iterator(path))
        # 设置合适的Content-Type和Content-Disposition
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Disposition'] = f'attachment;filename="{encoded_filename}"'
        # 添加文件大小到响应头（单位：字节）
        response['Content-Length'] = file_size
        response['Access-Control-Expose-Headers'] = 'Content-Length'

        return response

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

#增删查投标任务相关的业绩、团队
@timing_decorator
def api_modify_bid_relate(request):

    # 检查用户是否登录
    if not has_permission(request):
        # 如果用户未登录，抛出 AuthenticationFailed 异常
        return JsonResponse({'code': 0, 'msg': '未登录'})

    try:
        if request.method == 'PUT':

            # 获取参数
            # 获取并解析 JSON 请求体
            try:
                request_data = json.loads(request.body)
            except:
                return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

            # 获取参数
            bid_id = request_data.get('bid_id')
            data = request_data.get('data')
            _type = request_data.get('type')

            if not bid_id or not _type or not data:
                return JsonResponse({'code': 0, 'msg': '未携带投标任务id或任务类型或需添加的数据'})

            if _type not in ['project', 'pmProject', 'team']:
                return JsonResponse({'code': 0, 'msg': '类型只能为project、pmProject、team、tech、business'})

            #投标任务公司业绩添加
            if _type == 'pmProject':
                # 构建对象列表
                objects = [
                    ExBidPmProject(project_id=_id, bid_id=bid_id)
                    for _id in data
                ]

                # 批量插入数据库
                ExBidPmProject.objects.bulk_create(objects)
            elif _type == 'project':
                # 构建对象列表
                objects = [
                    ExBidProject(project_id=_id, bid_id=bid_id)
                    for _id in data
                ]

                # 批量插入数据库
                ExBidProject.objects.bulk_create(objects)
            elif _type == 'team':
                team_obj = ExTeam.objects.filter(account__in=data).distinct()
                # 构建对象列表
                objects = [
                    ExBidTeam(account=account, bid_id=bid_id)
                    for account in team_obj
                ]

                # 批量插入数据库
                ExBidTeam.objects.bulk_create(objects)

            return JsonResponse({'code': 1, 'msg': "新增成功"})

        elif request.method == 'DELETE':

            # 获取参数
            # 获取并解析 JSON 请求体
            try:
                request_data = json.loads(request.body)
            except:
                return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

            # 获取参数
            bid_id = request_data.get('bid_id')
            data = request_data.get('data')
            _type = request_data.get('type')

            if not bid_id or not _type or not data:
                return JsonResponse({'code': 0, 'msg': '未携带投标任务id或任务类型或需删除的数据'})

            if _type not in ['project', 'pmProject', 'team']:
                return JsonResponse({'code': 0, 'msg': '类型只能为project、pmProject、team、tech、business'})

            # 投标任务公司业绩添加
            if _type == 'pmProject':
                ExBidPmProject.objects.filter(bid=bid_id,project__in = data).delete()
            elif _type == 'project':
                ExBidProject.objects.filter(bid=bid_id,project__in = data).delete()
            elif _type == 'team':
                #删除之前判断一下是不是删除项目负责人，如果是，把项目负责人业绩也删除
                #先查询所有要删除的人员
                positions = ExBidTeam.objects.filter(bid=bid_id,account__in=data).values('position').distinct()
                for position in positions:
                    if position == '项目负责人':
                        #删除项目负责人业绩
                        ExBidPmProject.objects.filter(bid=bid_id).delete()
                        break

                ExBidTeam.objects.filter(bid=bid_id,account__in = data).delete()

            return JsonResponse({'code': 1, 'msg': '删除成功'})

        elif request.method == 'GET':

            # 获取参数
            bid_id = request.GET.get('bid_id')
            _type = request.GET.get('type')

            if not bid_id or not _type:
                return JsonResponse({'code': 0, 'msg': '未携带投标任务id或任务类型'})

            if _type not in ['project', 'pmProject', 'team']:
                return JsonResponse({'code': 0, 'msg': '类型只能为project、pmProject、team、tech、business'})

            #查询投标任务所属公司
            bid = ExBid.objects.filter(id=bid_id).select_related('createBy').first()

            # 投标任务公司业绩添加
            if _type == 'pmProject':
                #查询项目负责人
                account = ExBidTeam.objects.filter(bid=bid_id, position='项目负责人').values('account').first()

                #查询现有的负责人业绩
                now_project_ids = ExBidPmProject.objects.filter(bid=bid_id).values_list('project').distinct()

                # print(account, now_project_ids)

                #查询除了现有负责人业绩外其余业绩
                projects = ExProject.objects.filter(PM=account['account'], company=bid.createBy.company).\
                                                    exclude(id__in=now_project_ids).select_related('PM').distinct()

                format_projects = []
                for project in projects:
                    info = {
                        "id": project.id,
                        "name": project.name,
                        "code": project.code,
                        "PM": project.PM.name if project.PM else '',
                        "contractSignDate": project.contractSignDate.strftime("%Y年%m月%d日"),
                        "contractAmount": project.contractAmount,
                    }
                    format_projects.append(info)

                # print(len(format_projects))

                return JsonResponse({'code': 1, 'msg': '获取成功', 'data': format_projects})

            elif _type == 'project':

                # 查询现有的业绩
                now_project_ids = ExBidProject.objects.filter(bid=bid_id).values_list('project').distinct()

                # 查询除了现有负责人业绩外其余业绩
                projects = ExProject.objects.filter(company=bid.createBy.company).exclude(id__in=now_project_ids).select_related(
                    'PM').distinct()

                format_projects = []
                for project in projects:
                    info = {
                        "id": project.id,
                        "name": project.name,
                        "code": project.code,
                        "PM": project.PM.name if project.PM else '',
                        "contractSignDate": project.contractSignDate.strftime("%Y年%m月%d日"),
                        "contractAmount": project.contractAmount,
                    }
                    format_projects.append(info)

                # print(len(format_projects))

                return JsonResponse({'code': 1, 'msg': '获取成功', 'data': format_projects})
            elif _type == 'team':
                # 查询现有的团队成员
                now_team_accounts = ExBidTeam.objects.filter(bid=bid_id).values('account').distinct()

                # 查询除了现有负责人业绩外其余业绩
                team = ExTeam.objects.filter(company=bid.createBy.company).exclude(
                    account__in=now_team_accounts).select_related(
                    'dept').distinct()

                format_team = []
                for account in team:
                    info = {
                        "account": account.account,
                        "name": account.name,
                        "dept": account.dept.name if account.dept else '',
                        "degree": account.degree,
                        "professional": account.professional,
                        "workYears": account.workYears
                    }
                    format_team.append(info)

                # print(len(format_team))

                return JsonResponse({'code': 1, 'msg': '获取成功', 'data': format_team})

        else:
            res = {
                'code': 0,
                'msg': "不支持的请求方法"
            }
            return JsonResponse(res, status=405)


    except:
        return JsonResponse({'code': 0, 'msg': "修改出现了错误"})

#修改团队信息（职务和工作年限）
@timing_decorator
def api_modify_bid_team(request):
    if request.method == 'POST':

        # 检查用户是否登录
        # if not has_permission(request):
        #     # 如果用户未登录，抛出 AuthenticationFailed 异常
        #     return JsonResponse({'code': 0, 'msg': '未登录'})

        # 获取参数
        # 获取并解析 JSON 请求体
        try:
            request_data = json.loads(request.body)
        except:
            return JsonResponse({'code': 0, 'msg': '内容需要为json格式'})

        # 获取参数
        bid_id = request_data.get('bid_id')
        data = request_data.get('data')
        _type = request_data.get('type')

        if not bid_id or not _type or not data:
            return JsonResponse({'code': 0, 'msg': '未携带投标任务id或任务类型或需删除的数据'})

        if _type not in ['project', 'pmProject', 'team']:
            return JsonResponse({'code': 0, 'msg': '类型只能为project、pmProject、team、tech、business'})

        if _type == 'team':
            if 'account' not in data.keys() or not data['account']:
                return JsonResponse({'code': 0, 'msg': '未上传用户名'})

            account = data['account']

            #修改职务
            if 'position' in data.keys() and data['position']:
                if data['position'] == '项目负责人':
                    #需要先判断之前有没有项目负责人
                    pm = ExBidTeam.objects.filter(bid=bid_id, position='项目负责人').first()

                    if pm:
                        return JsonResponse({'code': 0, 'msg': '已经有项目负责人了'})

                # 修改之前判断一下是不是修改项目负责人，如果是，把项目负责人业绩也删除
                # 先查询所有要修改的人员
                position = ExBidTeam.objects.filter(bid=bid_id, account=account).values('position').distinct()
                if position == '项目负责人' and data['position'] != '项目负责人':
                    # 删除项目负责人业绩
                    ExBidPmProject.objects.filter(bid=bid_id).delete()

                ExBidTeam.objects.filter(bid=bid_id, account=account).update(position=data['position'])

            #修改工作年限
            if 'workYears' in data.keys() and data['workYears']:
                if not data['workYears'].isdigit():
                    return JsonResponse({'code': 0, 'msg': '工作年限不为数字'})

                ExTeam.objects.filter(account=account).update(workYears=data['workYears'])

            return JsonResponse({'code': 1, 'msg': '修改成功'})

    else:
        res = {
            'code': 0,
            'msg': "不支持的请求方法"
        }
        return JsonResponse(res, status=405)

